Canigó - Servei de Validació 2.2
SERVEI DE VALIDACIÓ
IntroduccióPropósitQuan realitzem una aplicació, existeixen sempre 2 tipus de validacions a considerar:
Inclou el tractament de l'entrada correcta dels tipus de dades, en la que no influeix l'estat de l'aplicació. Aquesta validació ha de tenir en compte, entre altres:
És aquella validació que depén de l'estat de l'aplicació, i de les dades de negoci. Aquesta validació queda fora de l'àmbit d'aquest document, perquè el seu tractament es realitzarà per mitjà del llançament i tractament d'excepcions de negoci (veure 'Servei d'Excepcions') canigo, encara que en la seva capa de presentació es basa en l'actualitat en l'ús de Struts, elimina l'ús dels ActionForm. Aquests només afegeixen complexitat innecessària i en un model vista controlador real hauriem de poder utilitzar el model de negoci des de les nostres pàgines. Per aquest motiu canigo proporciona els mecanismes necessaris per a fer ús de classes de negoci en presentació i elimina el cost de creació de formularis Struts. Així com en Struts la validació es realitza a nivell de presentació, en realitat el que s'està fent és anticipar la validació dels nostres objectes de negoci corresponents (un camp és requerit en presentació per què ho és en negoci, etc.). Podem finalitzar aquest discurs posant èmfasi que el Servei de Validació és un servei de propòsit general i no únicament de presentació. En tot cas, veurem en l'apartat 'Integració amb altres Serveis' com podem realitzar la validació en presentació comunicant-nos amb aquest servei. Context i Escenaris d'ÚsEl Servei de Validació es troba dins dels serveis de Propósit General de canigo.
Versions i DependènciesVersions:En la versió 1.1 s'ha afegit les validacions en el jsp. Dependències:Les dependències descrites a la següent url son requerides per tal de compilar i fer funcionar el projecte: A qui va dirigitAquest document va dirigit als següents perfils:
Documents i Fonts de Referència
GlossariCommons Validator Commons Validator és un estàndar de facto d'API de validació Descripció DetalladaArquitectura i Componentscanigo oferix la possibilitat d'utilitzar diferents implementacions de validació mitjançant la definició d'interfícies. En l'actualitat s'ofereix una implementació basada en l'ús de Spring Commons Validator. Els components podem classificar-los en:
Es pot trobar tota la documentació JavaDoc y el codi font referent aquests components a les següents urls: JavaDoc: http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-validation/apidocs/index.html Instal- lació i ConfiguracióInstal- lacióLa instal- lació del servei requereix de la utilització de la llibreria 'canigo-services-validation' i les dependències indicades a l'apartat 'Introducció-Versions i Dependències'. ConfiguracióLa configuració del Servei de Validació implica 3 pasos:
Definició de la Factoria Fitxer de configuració: canigo-services-validation.xml La factoria permet definir la configuració de la validació. En l'actualitat s'ofereix una implementació, basada en Commons i Spring:
Per aquesta classe definirem les següents propietats:
En l'atribut 'list' podem especificar 2 tipus de fitxers:
Aquest fitxer defineix les validacions disponibles per qualsevol formulari i la classe que resol la validació. En el cas de canigo, s'ofereix un fitxer bàsic 'validator-rules.xml' que conté les validacions més comuns.
Aquest fitxer permet definir per cada formulari o objecte quines validacions s'han de realitzar per cada camp o atribut. Exemple:
<bean name="validatorFactory" class="org.springmodules.commons.validator.DefaultValidatorFactory"> <property name="validationConfigLocations"> <list> <value>/WEB-INF/classes/validation/validator-rules.xml</value> <value>/WEB-INF/classes/validation/validation.xml</value> </list> </property> </bean> Definició del Servei Fitxer de configuració: canigo-services-validation.xml El bean 'validationService' és la interfície del servei que s'usarà des dels clients. En l'actualitat s'ofereix una implementació basada en Commons i Spring:
Exemple:
<bean id="validator" class="org.springmodules.commons.validator.DefaultBeanValidator"> <property name="validatorFactory"><ref bean="validatorFactory"/></property> </bean> <bean id="validationService" class="net.gencat.ctti.canigo.services.validation.commons. CommonsValidationServiceImpl"> <property name="validatorFactory"> <ref bean="validatorFactory" /> </property> <property name="i18nService"> <ref bean="i18nService" /> </property> <property name="loggingService"> <ref bean="loggingService" /> </property> </bean> Definició de les Regles de Validació Disponibles ![]() Fitxer sde configuració: validator-rules.xml Aquest fitxer defineix les validacions que es poden realitzar. En el fitxer 'validator-rules.xml' es defineixen els tipus de validacions que podem realitzar des de canigo. Aquestes regles de validació es basen en Spring i tenen la següent estructura:
<validator name="required" classname="org.springmodules.commons.validator.FieldChecks" method="validateRequired" methodParams="java.lang.Object, org.apache.commons.validator.ValidatorAction, org.apache.commons.validator.Field, org.springframework.validation.Errors" msg="errors.required"> <javascript><![CDATA[ function validateRequired(form) \{ var isValid = true; var focusField = null; ... ]]> </javascript> </validator> Cada validador defineix quina és la classe que realitza la validació (en el cas mostrat 'FieldChecks') i el mètode que es cridarà per validar el camp en el camp 'method'. Dins el tag '<javascript>' s'incorpora el codi que es generarà en el client per validar mitjançant Javascript (en el cas que la validació s'efectui des del client). Els validadors actualment disponibles són:
En el cas de voler definir un nou validador consultar la documentació de la classe 'FieldChecks'. Definició dels Fitxers de Validació Fitxers de configuració: validationXXX.xml Per a millor referència de l'ús dels validadors podem consultar http://struts.apache.org/struts-doc-1.2.4/userGuide/dev_validator.html. Tot i que la validació no es basa en 'Struts Validator' es segueix la mateixa política, pel que aquells que hi estiguin familiaritzats al seu ús podran ràpidament adaptar-se a l'ús de la validació. En els fitxers de validació definirem quines validacions volem realitzar per als nostres objectes o formularis. Definirem 2 seccions diferenciades:
Secció en la que es defineixen expressions regulars comunes que seran utilitzades des de diverses definicions de validació. Per exemple, la màscara corresponent a la comprovació del NIF podríem definir-la com una constant i ser referenciada en tots els camps a validar amb NIF.
<global> <constant> <constant-name>any</constant-name> <constant-value>^([0-9][0-9][0-9][0-9])$</constant-value> </constant> </global>
Encara que hem explicat que la validació serà a nivell d'objectes i/o formularis, Commons Validator utilitza el concepte form per a referir-se a ells. Per a cada camp especificarem quines validacions realitzar.
<formset> <form name="net.gencat.ctti.canigo.samples.jpetstore.persistence.Account"> <field property="firstname" depends="required,mask"> <arg0 key="nameForm.firstname.displayname"/> <msg name="mask" key="nameForm.errors.firstname.mask"/> <var><var-name>mask</var-name><var-value>^[a-zA-Z]*$</var-value></var> </field> <field property="lastname" depends="required"> <arg0 key="nameForm.lastname.displayname"/> </field> </form> </formset> L'esquema d'aquest fitxer és el mostrat a dalt. Cada objecte o formulari a validar serà definit en una secció '<form>' i cada camp del formulari en una subsecció '<field>' A l'atribut 'name' de la secció '<form>' definim el nom del validador. Per a cada camp especificarem:
Depenent del tipus de validacions definides incorporarem més propietats. A continuació detallarem per a cada tipus de validació els possibles atributs addicionals. Abans de començar a definir les validacions és important que entenguem que existeix una diferència entre validar formularis Web i validar objectes de negoci. La diferència entre el model Web i el model de classes és que en el model Web basat en HttpServletRequest tots els paràmetres rebuts són de tipus 'String', mentre que en les nostres classes de negoci fem servir altres tipus.
Aplicable a qualsevol tipus de valor
Pel fet que el missatge internacionalitzat requereix d'un paràmetre (indicat en {0}), podem passar-li el contingut a substituir per mitjà de la propietat 'arg0'
<arg0 key="nameForm.firstname.displayname"/>
Exemple: <field property="firstname" depends="required"> <arg0 key="nameForm.firstname.displayname"/>
_Aplicable a qualsevol tipus de valor _
En la configuració de validation.xml definirem les variables necessaries: Exemple:
<field property="parFixImport" depends="requiredif"> <arg0 key="validationForms.parametritzacio.ImportFix"/> <var> <var-name>field[0]</var-name> <var-value>partTipusRepartiment</var-value> </var> <var> <var-name>fieldTest[0]</var-name> <var-value>EQUAL</var-value> </var> <var> <var-name>fieldValue[0]</var-name> <var-value>1</var-value> </var> </field> En l'exemple anterior tenim una propietat del formulari, anomenada 'parFixImport', que depèn d'una altra propietat, 'partTipusRepartiment'. En aquest cas, si el camp partTipusRepartiment és igual a '1', 'parFixImport' serà requerit.
Aplicable a: Valors de tipus 'String'
En el tag '<var-name>' especificar 'mask' i en '<var-value>' la màscara a utilitzar.
<var> <var-name>mask</var-name> <var-value>^[a-zA-Z]*$</var-value> </var>
el fet que el missatge internacionalitzat requerix de 2 paràmetres podem especificar els valors per mitjà d'arguments del tipus 'arg0' i 'arg1'. Generalment, el missatge que no es compleix una màscara no és adequat per als usuaris. Per exemple: El camp 'Nom' no compleix la màscara '^[a-zA- Z]*$' segurament deixa atònits els nostres usuaris. Per aquest motiu és millor definir un missatge adequat al camp en qüestió per mitjà de la propietat 'msg':
<msg name="mask" key="nameForm.errors.firstname.mask"/> En cas de tenir que passar valors al nostre missatge definirem els arguments per mitjà de 'arg0',...'argN'. Podem definir màscares globals en la secció constants: <global> <constant> <constant-name>any</constant-name> <constant-value>^([0-9][0-9][0-9][0-9])$</constant-value> </constant> </global> i referenciar-les des de la variable '<var-value>' de la màscara per mitjà de '${nombreConstante}' <var> <var-name>mask</var-name> <var-value>$\{any\}</var-value> </var> Definir camps de tipus Data Aplicable a: Valors de tipus 'String'
La validació de tipus data es realitza així en cas que s'hagi definit l'atribut com a String. Si l'atribut és de tipus 'Date' no té sentit realitzar la validació sobre aquest tipus de dades, doncs aquest ja és un tipus vàlid. En cas que el camp sigui de tipus 'java.util.Date' no s'ha de definir la validació al fitxer. Veure 'Integració amb el Servei de Presentació' per veure cóm realitzar la validació si l'objecte no és de tipus String. NOTA: En cas d'utilitzar diferents formats de data (idiomes amb formats diferenciats) usar formsets. Veure 'Integració amb altres Serveis- Integració amb el Servei d'Internacionalització'.
Aplicable a valors de tipus 'String' Per comprovar si un valor entrat es podrà transformar en un valor numèric tenim vàries possibilitats segons el tipus de dades de destí al que anirà el valor entrat.
Aplicable a valors de tipus 'String'
el fet que el missatge internacionalitzat requerix de 2 paràmetres podem especificar els valors per mitjà d'arguments del tipus 'arg0' i 'arg1'. Segons el missatge mostrat l'argument '0' correspondrà a la clau del camp, mentre que l'argument '1' seria el nombre de caràcters permesos (maxlength, minlength). Per especificar que volem fer servir com a argument 1 el valor de variable utilitzat podem fer servir la sintaxi ${var: nomVariable} i especificar el parámetre 'resource=false' per indicar al validador que per aquesta clau el valor no s'ha d'obtenir del fitxer de recursos multiidioma, i per tant no s'ha de traduir.
<arg0 key="form.accountForm.name"/> <arg1 name="minlength" key="$\{var:minlength\}" resource="false"/> Utilització del ServeiLa utilització del Servei es basa principalment en la configuració. L'ús directe des dels clients es permet mitjançant les interfícies definides. canigo ofereix ja la integració des de 2 punts diferenciats:
En l'apartat 'Integració amb Altres Serveis' es dóna detall de cóm usar el Servei des de la capa de presentació, tot i que és necessària una lectura prèvia dels documents 'Serveis de Presentació' i 'Servei de Tags'. Eines de SuportL'expressió regular de les màscares definida a les validacions de tipus 'mask' es basa en PERL5. Si volem comprovar que s'ha definit correctament una expressió regular podem realitzar proves des de la pàgina 'http://jakarta.apache.org/oro/demo.html'. ![]() Integració amb Altres ServeisIntegració amb el Servei de InternacionalitzacióEn els fitxers de configuració es defineixen claus que permeten especificar quins missatges retornar en cas de validació errònia. Per a poder traduir aquestes claus és necessari especificar el Servei de Validació que usarà el Servei d'Internacionalització (veure Configuració). En determinats casos podem requerir de diferents tipus validació segons l'idioma seleccionat (dates, formats de moneda, etc.). En aquest cas, crearem diversos formsets.. Si fins ara haviem definit el nostre conjunt de validacions dins del tag '<formset'>, podrem agrupar diferents conjunts de validacions segons l'idioma per mitjà del format següent:
<formset language="xx">
</formset>
on 'xx' representa la codificació ISO del llenguatge segons l'estàndard especificat en: http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt De forma opcional podem especificar el país dins del llenguatge, la sintaxi seria:
<formset language="xx" country="yy"> </formset> on 'yy' representa la codificació ISO del país segons l'estàndard especificat en: http://userpage.chemie.fu-berlin.de/diverse/doc/ISO_3166.html Integració amb el Servei de PresentacióEl Servei de Presentació defineix la possibilitat de 2 tipus de validació: des de Client o Servidor. El tipus de validació que es farà servir es defineix al tag de formulari (consultar 'Servei de Tags - Tag Form). La diferència entre els 2 mecanismes és que en el cas de Client la validació es fa des del navegador del Client (mitjançant javascript) i en el cas de Servidor es fa servir Ajax per comunicar des del navegador amb el servidor i presentar els missatges sense haver de recarregar de nou la pàgina. En cas que la validació es faci per mitjà de Javascript incrustat a la pàgina, no cal realitzar cap procés adicional. El tag formulari s'encarrega de generar el codi de validació Javascript basat en el validador configurat. Si fem servir la validació des de Servidor amb Ajax, canigo proporciona una classe de validació des de Web que permet validar paràmetres de tipus Map. En aquest servei de validació, adicionalment es realitza el binding entre els paràmetres rebuts i l'objecte de destí. Per configurar aquesta validació realitzarem els següents pasos:
_ Fitxers de configuració: canigo-services-web.xml_ Cal que es referenciin els serveis d'internacionalització i logging, així com la factoria de validació ja definida pel Servei de Validació.
<bean id="webValidationService" class="net.gencat.ctti.canigo.services.web.validation. commons.CommonsWebValidationServiceImpl"> <property name="webDataBinderFactory"> <ref bean="webDataBinderFactory" /> </property> <property name="validatorFactory"> <ref bean="validatorFactory" /> </property> <property name="i18nService"> <ref bean="i18nService" /> </property> <property name="loggingService"> <ref bean="loggingService" /> </property> </bean>
_ Fitxers de configuració: canigo-services-web.xml_ <!- Use init method to initialize in 'init-method' -> <bean id="webDataBinderFactory" class="net.gencat.ctti.canigo.services.web.spring. bind.WebDataBinderFactory"> <property name="customEditors"> <ref bean="customEditors"/> </property> </bean> La propietat 'customEditors' permet definir cóm tractar cadascun dels tipus rebuts.
_ Fitxer de configuració: property-editors.xml_ En el cas de canigo s'ofereix un nou editor 'CustomDateEditor' que permet configurar cóm ha de ser la transformació de un String a Date. Per això permet configurar per cada llenguatge quin és el patró de data (basat en 'SimpleDateFormat') que ha de complir el paràmetre d'entrada per a poder fer la transformació.
<bean id="customEditors" class="java.util.HashMap"> <constructor-arg> <map> <entry key="java.util.Date"> <bean class="net.gencat.ctti.canigo.services.i18n.spring.beans.propertyeditors.CustomDateEditor"> <property name="i18nService" ref="i18nService"/> <property name="localeDatePatternsMap"> <map> <entry> <key><value>es</value></key> <value>dd/MM/yyyy</value> </entry> <entry> <key><value>en</value></key> <value>MM/dd/yyyy</value> </entry> </map> </property> </bean> </entry> </map> </constructor-arg> </bean> El missatge d'error associat que es mostrarà serà el que es defineixi al fitxer de recursos amb clau: 'typeMismatch.nomAtribut' Així, si volguessim mostrar l'error de validació de data per l'atribut 'birhDate', definiríem:
typeMismatch.birthDate=El camp 'Data de naixement' no és una data correcta
Una vegada realitzats els anteriors pasos, canigo proporciona al fitxer 'src/main/webapp/scripts/ajax/ajaxtags/canigo-ajaxtags-validation.js' el mecanisme de comunicació amb el servei. En la generació de la pàgina s'afegeix com a listener del submit del formulari i realitza la crida al servidor per a comprovar les validacions. Si tot és correcte farà el submit, en cas contrari presentarà els missatges d'error. Per a presentar els missatges d'error requereix que es defineixi en alguna part de la pàgina:
Un exemple de pàgina seria la següent:
<div class="error" id="errorsZone" style="margin-right: 10px; margin-bottom: 3px; margin-top: 3px;display:none"> <img src="images/iconWarning.gif" class="icon" alt="Warning" /> Se han encontrado <span id="errorCount"></span> errores en tu formulario. <p>Por favor corrige estos errores y vuelve a enviar el formulario:</p> <ul id="errorList"></ul> </div> NOTA: Podem substituir amb el tag 'fmt:message' de JSTL el missatge 'Se han encontrado...' Si definim els següents estils per mostrar els resultats: div.error, div.message { background: #ffc; border: 1px solid #000; color: #000000; font-family: Arial, Helvetica, sans-serif; font-size: 1em; font-weight: normal; margin: 10px auto; padding: 3px; text-align: left; vertical-align: bottom; margin-right: 200px; } El resultat obtingut seria el següent: ![]() |